Replace the loose helpers.py functions with the PromptLibrary class in vault.py and app.py.
Day 45 of 80
You have two new classes — Prompt and PromptLibrary. Now you put them to work. Today you update the CLI tool (vault.py) to use them instead of raw dictionaries and loose functions.
The app's behavior won't change. The code will be significantly cleaner.
Replace the contents of vault.py with this version. Notice how much cleaner the main loop is now that the library manages itself:
import anthropic
from dotenv import load_dotenv
from models import Prompt, PromptLibrary
load_dotenv()
client = anthropic.Anthropic()
# One object manages everything. No more importing load_prompts/save_prompts.
library = PromptLibrary("prompts.json")
def generate_prompt_text(shot, platform):
"""Call Claude to generate a prompt. Returns the text."""
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=300,
system="You are an expert AI video prompt engineer.",
messages=[{
"role": "user",
"content": f"Write a production-ready {platform} AI video prompt for: {shot}. "
f"Include camera, lighting, mood. Under 100 words. Return only the prompt."
}]
)
return message.content[0].text
print("=== DVP Prompt Vault ===\n")
while True:
print(f"Library: {len(library)} prompts")
print("1. View all 2. Generate 3. Search 4. Filter 5. Delete 6. Quit")
choice = input("\nChoose: ")
if choice == "1":
if not library.prompts:
print("\n (empty)\n")
else:
for i, p in enumerate(library):
# p is a Prompt object — print(p) uses __str__
print(f" {i + 1}. {p}")
print(f" {p.preview(60)}\n")
elif choice == "2":
shot = input("Describe your shot: ")
raw_platform = input("Platform (Kling / Runway / Veo): ")
try:
text = generate_prompt_text(shot, raw_platform)
print(f"\n{text}\n")
if input("Save? (y/n): ").lower() == "y":
# Prompt() validates the platform — raises ValueError if invalid
prompt = Prompt(raw_platform, shot, text)
library.add(prompt)
print("Saved!\n")
except ValueError as e:
print(f" {e}\n")
except Exception as e:
print(f" API error: {e}\n")
elif choice == "3":
query = input("Search: ")
results = library.search(query)
print(f"\n {len(results)} match{'es' if len(results) != 1 else ''}")
for p in results:
print(f" {p}")
print()
elif choice == "4":
platform = input("Filter by platform (Kling / Runway / Veo): ")
results = library.filter_by_platform(platform)
print(f"\n {len(results)} {platform} prompts")
for p in results:
print(f" {p}")
print()
elif choice == "5":
idx = input("Delete prompt # (or 'cancel'): ")
if idx.lower() != "cancel":
try:
removed = library.delete(int(idx) - 1)
if removed:
print(f" Deleted: {removed}\n")
else:
print(" Invalid number.\n")
except ValueError:
print(" Enter a number.\n")
elif choice == "6":
print("Bye!")
break
The main loop is simpler now. Instead of calling load_prompts() at the start and save_prompts() after every change, the library handles persistence internally. You just call library.add() or library.delete() and the file stays in sync.
Notice how validation moved. In the old code, platform validation was scattered across different functions. Now Prompt(raw_platform, shot, text) raises a ValueError if the platform is wrong — the class enforces its own rules.
for i, p in enumerate(library): works because you added __iter__ to PromptLibrary. The library behaves like a list. Clean, readable, Pythonic.
If you want the bonus challenge, update your Flask app.py to use PromptLibrary too. The key change is switching from load_prompts() / save_prompts() to a PromptLibrary instance. Here's the pattern:
# Before (helpers.py approach):
from helpers import load_prompts, save_prompts
prompts = load_prompts()
# ... do stuff ...
save_prompts(prompts)
# After (PromptLibrary approach):
from models import Prompt, PromptLibrary
library = PromptLibrary("prompts.json")
# ... do stuff ...
# no manual save — library.add() and library.delete() handle it
# In your routes, access prompts with:
library.prompts # the full list
library.search(q) # search results
library.filter_by_platform(p) # filtered list
library.platform_counts() # for the stats bar
# In Jinja2 templates, switch from p['platform'] to p.platform
# (dot notation works because Prompt objects have attributes, not keys)
You now think in objects. A Prompt isn't just a dictionary — it validates itself, formats itself, and serializes itself. A PromptLibrary manages a collection with a clean API.
This is the shift from "scripting" to "software engineering."
vault.py uses PromptLibrary — no direct load_prompts() / save_prompts() calls__len__ and __iter__ doadd() calls save() automaticallyapp.py updated to use PromptLibrary